﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace personalplaner.common.threading
{

	/// 
	/// this class was created by: Dwayne Need 
	/// Description:
	/// http://blogs.msdn.com/b/dwayneneed/archive/2007/04/26/multithreaded-ui-hostvisual.aspx
	/// 
	/// Download:
	/// http://microsoftdwayneneed.codeplex.com/SourceControl/list/changesets
	/// 
	/// For more information look at 
	/// personalplaner.common.controls.VisualWraper.cs
	/// personalplaner.common.threading.VisualTargetPresentationSource.cs
	/// 
	/// personalplaner.common.controls.VisualWraper readme.txt
	/// 

	/// <summary>
	///     The VisualTargetPresentationSource represents the root
	///     of a visual subtree owned by a different thread that the
	///     visual tree in which is is displayed.
	/// </summary>
	/// <remarks>
	///     A HostVisual belongs to the same UI thread that owns the
	///     visual tree in which it resides.
	///     
	///     A HostVisual can reference a VisualTarget owned by another
	///     thread.
	///     
	///     A VisualTarget has a root visual.
	///     
	///     VisualTargetPresentationSource wraps the VisualTarget and
	///     enables basic functionality like Loaded, which depends on
	///     a PresentationSource being available.
	/// </remarks>
	public class VisualTargetPresentationSource : PresentationSource
	{
		public VisualTargetPresentationSource(HostVisual hostVisual)
		{
			_visualTarget = new VisualTarget(hostVisual);
		}

		public override Visual RootVisual
		{
			get
			{
				return _visualTarget.RootVisual;
			}

			set
			{
				Visual oldRoot = _visualTarget.RootVisual;


				// Set the root visual of the VisualTarget.  This visual will
				// now be used to visually compose the scene.
				_visualTarget.RootVisual = value;

				// Hook the SizeChanged event on framework elements for all
				// future changed to the layout size of our root, and manually
				// trigger a size change.
				FrameworkElement rootFE = value as FrameworkElement;
				if (rootFE != null)
				{
					rootFE.SizeChanged += new SizeChangedEventHandler(root_SizeChanged);
					rootFE.DataContext = _dataContext;

					// HACK!
					if (_propertyName != null)
					{
						Binding myBinding = new Binding(_propertyName);
						myBinding.Source = _dataContext;
						rootFE.SetBinding(TextBlock.TextProperty, myBinding);
					}
				}

				// Tell the PresentationSource that the root visual has
				// changed.  This kicks off a bunch of stuff like the
				// Loaded event.
				RootChanged(oldRoot, value);

                // Kickoff layout...
                UIElement rootElement = value as UIElement;
                if (rootElement != null)
                {
                    //rootElement.Measure(new Size(0, 0));

                    rootElement.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                    rootElement.Arrange(new Rect(rootElement.DesiredSize));

                    //rootElement.Measure(new Size(0,0));
                    //rootElement.Arrange(new Rect(new Size(0,0)));
                }
			}
		}

		public object DataContext
		{
			get { return _dataContext; }
			set
			{
				_dataContext = value;
				var rootElement = _visualTarget.RootVisual as FrameworkElement;
				if (rootElement != null)
				{
					rootElement.DataContext = _dataContext;
				}
			}
		}

		// HACK!
		public string PropertyName
		{
			get { return _propertyName; }
			set
			{
				_propertyName = value;

				var rootElement = _visualTarget.RootVisual as TextBlock;
				if (rootElement != null)
				{
					if (!rootElement.CheckAccess())
					{
						throw new InvalidOperationException("What?");
					}

					Binding myBinding = new Binding(_propertyName);
					myBinding.Source = _dataContext;
					rootElement.SetBinding(TextBlock.TextProperty, myBinding);
				}
			}
		}

		public event SizeChangedEventHandler SizeChanged;

		public override bool IsDisposed
		{
			get
			{
				// We don't support disposing this object.
				return false;
			}
		}

		protected override CompositionTarget GetCompositionTargetCore()
		{
			return _visualTarget;
		}

		private void root_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			SizeChangedEventHandler handler = SizeChanged;
			if (handler != null)
			{
				handler(this, e);
			}

            RootVisual.Dispatcher.Invoke(new Action(delegate()
            {
                var element = RootVisual as FrameworkElement;
                if (element != null)
                {
                    element.Width = e.NewSize.Width;
                    element.Height = e.NewSize.Height;
                }

            }));
		}

		private VisualTarget _visualTarget;
		private object _dataContext;
		private string _propertyName;
	}
}
